# Game.py: Alien Xenocide main module
# Written by Victor Luna Freire - victorclf@live.com
# Copyright (c) Victor Luna Freire. All rights reserved.
# Sunday, June 04, 2009 00:00:00 PM
# Python 2.6.2

import os
import sys
import pygame
import random

import Sprites
import Gameobjects
import Timer
import Input
import Raycaster
import Maps
import Weapons
import Defines
import Stars
import ScreenFX
import Shared
import Renderer
import Gamescript
import Sound
import Maze

import Menu
import Builder
import Solver

try:
    import psyco #psyco should be the last import
    psyco.full()
except ImportError:
    print "Performance Warning: psyco not available!"

    
class FPS_counter:
    def __init__(self):
        self.frames = 0
    def average(self):
        return self.frames / (pygame.time.get_ticks() / 1000.)


class Game(Menu.Menu, Builder.Builder, Solver.Solver):
    def __init__(self):
        ''' Initializes the game data and modules. '''

        Sound.Sound.pre_init()       
        
        # Pygame initialization
        pygame.init()
        
        pygame.mixer.set_num_channels(32)
        pygame.mixer.music.set_volume(1.0)
        # Sound init        
        Sound.Sound.load_sounds()
                
        # Set window caption and icon
        pygame.display.set_caption(Defines.WINDOW_TITLE)
        icon = pygame.image.load(os.path.join('data', 'sprites', 'ax.png'))
        if icon: pygame.display.set_icon(icon)
        else: warn("Failed to load window icon!")
    
        screen_flags = Defines.SCREEN_FLAGS
        if len(sys.argv) > 1:
            if sys.argv[1] == '-fullscreen':
                screen_flags = pygame.FULLSCREEN
            elif sys.argv[1] == '-window':
                screen_flags = 0
                
        # Pygame display configuration
        try:
            self.screen = pygame.display.set_mode((Defines.SCREEN_WIDTH, Defines.SCREEN_HEIGHT), screen_flags, Defines.SCREEN_BPP)
        except pygame.error, msg:
            print "Error: failed to initialize display -", msg
                
        # Hide mouse
        pygame.mouse.set_visible(False)
        
        # Sprites Manager
        Sprites.Sprites.load_sprites()
                
        # Raycasting renderer
        self.Raycaster = Raycaster.RaycasterClass(Defines.PROJECTION_WIDTH, Defines.PROJECTION_HEIGHT)
        
        # The Player
        self.player = Gameobjects.Player((Defines.MAP_START_POS_X, Defines.MAP_START_POS_Y), 45)
        
        # The Guns
        #self.Weapons = Weapons.Weapons()
                
        # Setup map system(map system loads stuff automatically)
        #Maps.Maps.set_current_map('map0')
        
        # Load the States
        Menu.Menu.__init__(self)
        Builder.Builder.__init__(self)
        Solver.Solver.__init__(self)
        
        # Timer multiply factor
        self.mult_factor = 0
        # Creates the state stack
        self.state = [self.main_menu] 
        
        Sound.Sound.play_music('menu')
        Gamescript.SoundDirector.sound_event('titlecall')
        self.reset_menu()
                
        #self.hud_font = pygame.font.SysFont('ALIEN5.TTF',26)
        #self.hud_font2 = pygame.font.SysFont('ALIEN5.TTF',32)
        #self.hud_deck_surface = self.hud_font.render("DECK", True, (255,255,255))
        
        # Display new game tutorial?
        self.tutorial = Defines.NEW_GAME_TUTORIAL
        
        self.show_compass = True
        

    def shutdown(self):
        pygame.quit()
        
    
    def new_game(self):
        #Maps.Maps.maps = {}
        #Maps.Maps.objects = {}
        #Maps.Maps.load_maps()
        self.found_exit = False
        mazecopy = Maze.Maze.get_maze_copy()
        mazecopy[-1][-2] = 'Z' #exit
        Maps.Maps.load_temp_map(mazecopy)
        self.reset_menu()
        Sound.Sound.play_music('maze')
        #self.Weapons.recharge()
        self.player.health = self.player.max_health
        Gamescript.GameDirector.new_game(self.player)
        self.state.append(self.game)
        self.death_time_elapsed = 0
        self.compass_variation = random.randint(0, 359)
        if(Defines.NEW_GAME_TUTORIAL):
            self.tutorial = True
            Sprites.Sprites.get_sprite('ui\\fasttutorial').set_alpha(210)
    
    
    
    def render_world(self, objects):
        ''' Renders the world. '''
        self.Raycaster.render_scene(self.screen, self.player, Gamescript.GameDirector.objects)
    
    
    def process_world_objects(self):
        ''' Handles world objects. Returns the objects processed. '''
        objects = Gamescript.GameDirector.objects if Defines.ENABLE_WORLD_OBJECTS else []
        for o in objects:
            o.main(self.player, self.mult_factor, self.time_variation)
        return objects
    
    
    def process_world(self):
        ''' Process world changes and draws it. '''
        self.render_world(self.process_world_objects())
        
    
    def process_compass(self):
        ''' Handles the compass. '''
        if self.show_compass:
            self.screen.blit(Sprites.Sprites.get_sprite('ui\\compass'), (250, 200))
            # The compass needle
            needlesprite = Sprites.Sprites.get_sprite('ui\\needle') 
            orig_rect = needlesprite.get_rect()
            orig_rect.topleft = 480, 289
                    
            needlerotated = pygame.transform.rotate(needlesprite, (360 - self.player.angle) + self.compass_variation)
            rect = needlerotated.get_rect(center=orig_rect.center)
            self.screen.blit(needlerotated, rect)
        else:
            self.screen.blit(Sprites.Sprites.get_sprite('ui\\compass'), (250, 400))
            

    def game(self):
        if not self.game_input(self.mult_factor):
            Sound.Sound.play_music('menu')
            return False
        
        # Clear screen
        self.screen.fill((0, 0, 0)) 
        
        # Process the world changes and render the world view
        self.process_world()
        
        # Process the player's compass
        self.process_compass()
                
        if self.found_exit: self.screen.blit(Sprites.Sprites.get_sprite('menu\\enddoor'), (0, 0))
        #self.Weapons.draw(self.screen)
        #self.draw_hud()
        
        #ScreenFX.ShieldHit.main(self.screen, self.mult_factor)
        
        if self.tutorial:
            self.screen.blit(Sprites.Sprites.get_sprite('ui\\fasttutorial'), (0, 0))
        
        #if self.player.health < 1:
        #    if not(ScreenFX.BloodDeath.finished or ScreenFX.BloodDeath.running):
        #        ScreenFX.BloodDeath.reset()
        #    
        #    ScreenFX.BloodDeath.update()
        #    ScreenFX.BloodDeath.draw(self.screen)
        #    
        #    self.death_time_elapsed += self.time_variation
        #    if self.death_time_elapsed > Defines.DEAD_SCREEN_TIME:
        #        Sound.Sound.play_music('menu')
        #        return False
                
        pygame.display.flip()
        return True
        
        
    def draw_hud(self):
        ''' Draws the player's HUD. '''
        pass
        #self.screen.blit(Sprites.Sprites.get_sprite('ui\\barra_status'), (0, Defines.SCREEN_HEIGHT - 80))
        #
        #self.screen.blit(self.hud_deck_surface, (570,408))
        #self.screen.blit(self.hud_font2.render(str(Gamescript.GameDirector.current_map + 1), True, (255,255,255)), (587,432))
        #
        #shield_y = 408
        #shieldbar_x = 65
        #shieldbar_y = 410
        #self.screen.blit(Sprites.Sprites.get_sprite('ui\\shield'), (17, shield_y))
        #self.screen.fill((0,0,0), (shieldbar_x, shieldbar_y, 299, 30))
        #self.screen.blit(Sprites.Sprites.get_sprite('ui\\shieldbar'), (65, shieldbar_y), (0, 0, 300 * (self.player.health / float(self.player.max_health)), 30))
        #
        #shellbox_y = 443
        #shells_x = 65
        #shells_y = 445
        #shell_width = 8
        #shell_proportion = 2
        #self.screen.blit(Sprites.Sprites.get_sprite('ui\\shellbox'), (10, shells_y))
        #self.screen.fill((0,0,0), (shells_x, shells_y+1, shell_width * (self.Weapons.current_weapon.max_ammo / shell_proportion) + 1, 29))
        #shell_sprite = Sprites.Sprites.get_sprite('ui\\shotshell')
        #for i in xrange(0, self.Weapons.current_weapon.ammo, 2): self.screen.blit(shell_sprite, (shells_x + ((i/shell_proportion)*8), shells_y + 2))
        
        
        
    def interact_map(self):
        ''' Processes interactions with the world by the player. '''        
        gridposplayer = Renderer.togridF(self.player.x, self.player.y)
        for xvar in xrange(-1, 2):
            for yvar in xrange(-1, 2):
                gridpos = (gridposplayer[0] + xvar, gridposplayer[1] + yvar)
                if Maps.Maps.is_valid_grid(gridpos):
                    wid = Maps.Maps.world[gridpos[1]][gridpos[0]]
                    if wid == '0':   #empty space
                        continue
                    elif wid == 'Z': #level exit
                        Gamescript.SoundDirector.sound_event('door')
                        Sound.Sound.play_music('victory')
                        self.found_exit = True
                        #Gamescript.GameDirector.found_exit(self.player)
                        return
                    elif wid == 'W': #loaded recharge station
                        #Maps.Maps.world[gridpos[1]][gridpos[0]] = 'X' #empty this station
                        #Gamescript.SoundDirector.sound_event('recharge')
                        #self.player.health = self.player.max_health
                        #self.Weapons.recharge()
                        return
    
    
    def game_input(self, mult_factor):
        keep_running = Input.Input.get_input()
        
        if Input.Input.keys:
            self.tutorial = False
        
        if Input.Input.check_once(pygame.K_ESCAPE):
            keep_running = False
        
        
        #Is the Player alive?
        if self.player.health < 1 or self.found_exit: return keep_running
        
        mult_factor_run = mult_factor
        if Input.Input.check(pygame.K_LSHIFT):
            mult_factor *= 2  # player running?
            mult_factor_run *= 1.3  # player running?
        
        if Input.Input.check(pygame.K_UP):
            self.player.move_forward(mult_factor_run)
            
        if Input.Input.check(pygame.K_DOWN):
            self.player.move_backward(mult_factor_run)
            
        if Input.Input.check(pygame.K_LEFT):
            if Input.Input.check(pygame.K_LALT):
                self.player.strafe_left(mult_factor_run)
            else:
                self.player.turn_left(mult_factor)

        if Input.Input.check(pygame.K_RIGHT):
            if Input.Input.check(pygame.K_LALT):
                self.player.strafe_right(mult_factor_run)
            else:
                self.player.turn_right(mult_factor)
                
        if Input.Input.check_once(pygame.K_SPACE):
            if not self.found_exit: self.interact_map()
            
        if Input.Input.check_once(pygame.K_s):
            Gamescript.SoundDirector.sound_event('solving')
            self.new_solver()
            
        if Input.Input.check_once(pygame.K_c):
            self.show_compass = not self.show_compass
        
        #if Input.Input.check(pygame.K_LCTRL):
        #    self.Weapons.fire(self.Raycaster.enemies_in_aim)
        #
                    
        #if Input.Input.check_once(pygame.K_p) and Defines.MONSTEREDIT:
        #    MonsterEditor.addp1((self.player.x, self.player.y))
        #
        #if Input.Input.check_once(pygame.K_l) and Defines.MONSTEREDIT:
        #    MonsterEditor.addp2((self.player.x, self.player.y))
        #
        #if Input.Input.check_once(pygame.K_n) and Defines.MONSTEREDIT:
        #    Gamescript.GameDirector.found_exit(self.player)
        
        return keep_running


def main():
    game = Game() # creates the game object and initializes the modules
    while game.state:
        game.mult_factor = Timer.Timer.get_mult_factor()
        game.time_variation = Timer.Timer.get_time_variation()
        if not game.state[-1]():
            game.state.pop()
        Timer.Timer.update()
    game.shutdown()   # cleans up before exitting
    print "Thanks for playing Graphaze."    

main()
